#ifndef __MINIKIT_SOCK_H__
#define __MINIKIT_SOCK_H__
#include <pthread.h>
#include <sys/types.h>
#include <string>
#include <exception>
#include <netinet/in.h>      // For sockaddr_in

namespace MINIKIT
{

class SocketException: public std::exception
{
public:
    SocketException( const std::string& msg, bool syserr=false );
    virtual ~SocketException() throw( );
    virtual const char* what() const throw( );
protected:
    std::string m_message;
};//class SocketException

/*
 * case1 "tcp-server-listen-socket" bind, listen, accept
 * case2 "tcp-server-dealing-socket" recv, send, close
 * case3 "tcp-client-socket" connect, send, recv, close
 * */

/*
 * case1 "udp-server-socket" bind, recvfrom, sendto
 * case2 "udp-client-socket" sendto, recvfrom
 * */

/*
 * socket:: send, recv
 *   --tcpsocket:: send, recv, close | all kinds of setting
 *   --udpsocket:: send, recv | all kinds of setting
 * tcplistener:: bind, listen, accept
 * tcpserver>tcpsocket:: recv, send, close | removeAddr
 * tcpclient>tcpsocket:: connect, send, recv, close
 * udpclient>udpsocket:: send, recv
 * udpserver>udpsocket:: recv, send
 * */

class Socket;
class TCPSocket;
class TCPListener;
class TCPServerSocket;
class TCPClientSocket;
class UDPSocket;
class UDPServerSocket;
class UDPClientSocket;
typedef int32_t(FUNC_SOCK_ACTION)( int32_t, const sockaddr*, socklen_t, Socket* cls);

class Socket
{
public:
    virtual ~Socket();
private:
    Socket(const Socket& sock);
    void operator=(const Socket& sock);
protected:
    Socket(int32_t type, int32_t protocol) throw(SocketException);
    Socket(int32_t fd);
public:
    void  setNonBlock();
    void  setReuseAddr();
    void  setLinger(bool on, int32_t sec_delay=0);
    void  setTimeout(size_t sec, size_t usec);
    void  setConnTimeout(size_t sec, size_t usec);
    void  setRecvTimeout(size_t sec, size_t usec);
public:
    void setupSocket_DO( const std::string& addr, int32_t port, FUNC_SOCK_ACTION func_action, sockaddr &sa, socklen_t &sa_len, bool is_passive ) throw(SocketException); 
    const sockaddr& getLocalSA() throw(SocketException); 
    const sockaddr& getRemoteSA() throw(SocketException);
    std::string getLocalAddr()  throw(SocketException);
    int32_t     getLocalPort()  throw(SocketException);
    std::string getRemoteAddr() throw(SocketException);
    int32_t     getRemotePort() throw(SocketException);
public:
    static void*    toINAddr(const sockaddr &sa);
    static uint16_t toINPort(const sockaddr &sa);
    static std::string toAddr(const sockaddr &sa);
    static uint16_t    toPort(const sockaddr &sa);

protected:
    //int32_t   so_domain;
    int32_t   so_type;
    int32_t   so_protocol;
    int32_t   fd;
    sockaddr *sa_local;
    sockaddr *sa_remote;
    timeval   tm_conn;
    timeval   tm_recv;
protected: //implemetation of FUNC_SOCK_ACTION
    static int32_t func_sock_nothing( int32_t fd, const sockaddr* sa, socklen_t sa_len, Socket* cls);
    static int32_t bind_wrap( int32_t fd, const sockaddr* sa, socklen_t sa_len, Socket* cls);
    static int32_t connect_timeout_wrap( int32_t fd, const sockaddr* sa, socklen_t sa_len, Socket* cls);
};//class Socket

class TCPSocket: public Socket
{
protected:
    TCPSocket() throw(SocketException);
    TCPSocket(int32_t fd);
public:
    int32_t send( const void* buf, size_t len ) throw(SocketException);
    int32_t recv( void*       buf, size_t len ) throw(SocketException);
    int32_t close();
//SocketOption??
};//class TCPSocket

class TCPListener: public Socket
{
public:
    TCPListener() throw(SocketException);
public:
    int32_t          bind(const std::string &addr, int32_t port) throw(SocketException);
    int32_t          listen(size_t maxpending) throw(SocketException);
    TCPServerSocket* accept() throw(SocketException);

};//class TCPListener

class TCPServerSocket: public TCPSocket 
{
public:
private:
    //access for TCPListener::accept 
    friend class TCPListener;
    TCPServerSocket();
    TCPServerSocket(int32_t fd);
    
};//class TCPServerSocket

class TCPClientSocket: public TCPSocket 
{
public:
    TCPClientSocket() throw(SocketException);
    int32_t connect(const std::string &addr, int32_t port) throw(SocketException);
};//class TCPClientSocket


class UDPSocket: public Socket
{
protected:
    UDPSocket() throw(SocketException);
    UDPSocket(int32_t fd);
public:
    int32_t sendTo( const void* buf, size_t len, const sockaddr *remote_sa, socklen_t remote_sa_len ) throw(SocketException);
    int32_t recvFrom( void* buf, size_t len, sockaddr *remote_sa, socklen_t *remote_sa_len ) throw(SocketException);
    int32_t sendTo( const void* buf, size_t len, const std::string &remoteAddr, int32_t remotePort ) throw(SocketException);
    int32_t recvFrom( void* buf, size_t len, std::string &remoteAddr, int32_t &remotePort ) throw(SocketException);
    //kinds of setting
    
//SocketOption??
};//class UDPSocket

class UDPServerSocket: public UDPSocket 
{
public:
    int32_t bind(const std::string &addr, int32_t port) throw(SocketException);
    //int32_t send( const char* buf, size_t len );
    //int32_t recv( char* buf, size_t len );
//protected:
//    UDPSocket *udpsock;
};//class UDPServerSocket

class UDPClientSocket: public UDPSocket 
{
public:
    //int32_t send( const char* buf, size_t len );
    //int32_t recv( char* buf, size_t len );
//protected:
//    UDPSocket *udpsock;
};//class UDPClientSocket


};//namespace MINIKIT



#endif /*__MINIKIT_SOCK_H__*/
